# -*- coding: utf-8 -*-

import sys
import event
import cPickle
import copy
from common import *
from event import CONTINUE_HANDLE
import player
import netstream

class Filter:
    def __init__(self):
        self.__attached_dispatcher = {}
        
    def match(self, event_id):
        return False
    
    def handle(self, event):
        return [event]
    
    def attached(self, dispatcher, event_id):
        if not event_id in self.__attached_dispatcher:
            self.__attached_dispatcher[event_id] = []
        self.__attached_dispatcher[event_id].append(dispatcher)
        
    def deattached(self, dispatcher, event_id):
        if not event_id in self.__attached_dispatcher:
            return
        try:
            self.__attached_dispatcher.remove(dispatcher)
        except ValueError,data:
            return

class QuitFilter(Filter):
    def match(self, event_id):
        return True
    def handle(self, h_event):
        h_event.set_state(event.HANDLED)
        sys.exit()
        
class ClientUpdateFilter(Filter):
    def __init__(self, update_groups, draw_group):
        Filter.__init__(self)
        self.update_groups = update_groups
        self.draw_group = draw_group
    def match(self, event_id):
        if event_id == event.FrameEvent.event_id:
            return True
        return False
    def handle(self, h_event):
        h_event.set_state(event.CONTINUE_HANDLE)
        for update_group in self.update_groups:
            update_group.update()
            
        dirtys = []
        screen = pygame.display.get_surface()
        dirtys = self.draw_group.draw(screen)
#        for draw_group in self.draw_groups:
#            dirty_r = None
#            dirty_r = draw_group.draw(screen)
#            if dirty_r:
#                dirtys += dirty_r
                
#        pygame.display.update(dirtys)
        TIME_STAMPER.update()
        return [h_event]

class ClientEventHandleFilter(Filter):
    def __init__(self, player_center):
        Filter.__init__(self)
        self.handles = {
                        event.MoveEvent.event_id: self.__move_handle,
                        event.PlayerOnLineEvent.event_id : self.__player_online_handle
                        }
        self.player_center = player_center
    
    def __check_need_data(self, h_event, need_data_name):    
        for data_name in need_data_name:
            if data_name in h_event.extra_data:
                h_event.set_state(event.ERROR_HAPPENED)
                h_event.error_msg = '%s is not in extra data.' % data_name
                return False
        return True
            
    def __move_handle(self, h_event):
#        need_data = [D_PLAYER_ID, D_POS]
#        if not self.__check_need_data(h_event, need_data):
#            return
        player_id = h_event.id
        pos = h_event.pos
        player = self.player_center.find_player(player_id)
        if not player:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = "Can't find player %d." % player_id
            return
        player.walkto(pos)
        h_event.set_state(event.HANDLED)
    
    def __player_online_handle(self, h_event):
        pl = player.PlayerSprite(h_event.id, h_event.hp, h_event.pos)
        self.player_center.add_player(pl)
    
    def match(self, event_id):
        return True
    def handle(self, h_event):
        e_type = h_event.type()
        if e_type in self.handles:
            self.handles[e_type](h_event)
        else:
            h_event.set_state(event.CONTINUE_HANDLE)
        return [h_event]
    
class ServerEventHandleFilter(Filter):
    def __init__(self, event_producer):
        Filter.__init__(self)
        self.handles = {
                        event.MoveEvent.event_id : self.__move_handle,
                        event.RegEvent.event_id : self.__reg_handle,
                        event.LoginEvent.event_id : self.__login_handle
                        }
        self.event_producer = event_producer
    
    def __move_handle(self, h_event):
        
        h_event.set_state(event.HANDLED)
        
        new_event = copy.copy(h_event)
        new_event.set_state(event.CONTINUE_HANDLE)
        
        player_center = player.get_player_center()
        pl = player_center.find_player(h_event.id)
        if not pl:
            return
        pl.walkto(h_event.pos)
        
        online_ids = player_center.online_ids()
        new_event.extra_data[D_TO_HIDS] = [player_center.get_hid(id) for id in online_ids]
        
        self.event_producer.add_oneshot_event(new_event)
        
    def __reg_handle(self, h_event):
        h_event.set_state(event.HANDLED)
        
        if not D_FROM_HID in h_event.extra_data:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = '%s is not in extra data.' % D_FROM_HID
            return
            
        from_hid = h_event.extra_data[D_FROM_HID]
        
        player_center = player.get_player_center()
        new_player = player_center.reg(h_event.name, h_event.role)
        
        confirm_event = None
        if not new_player:
            confirm_event = event.ConfirmEvent(event.STATUS_FAIL)
        else:
            confirm_event = event.ConfirmEvent(event.STATUS_SUCC)
        confirm_event.extra_data[D_TO_HIDS] = [from_hid]
            
        self.event_producer.add_oneshot_event(confirm_event)
        
    def __login_handle(self, h_event):
        h_event.set_state(event.HANDLED)
        
        if not D_FROM_HID in h_event.extra_data:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = '%s is not in extra data.' % D_FROM_HID
            return
        
        from_hid = h_event.extra_data[D_FROM_HID]
        
        player_center = player.get_player_center()
        pl = player_center.login(h_event.name, from_hid)
        
        confirm_event = None
        if not pl:
            confirm_event = event.ConfirmEvent(event.STATUS_FAIL)
        else:
            confirm_event = event.ConfirmEvent(event.STATUS_SUCC)
            confirm_event.extra_data[D_ID] = pl.id
            confirm_event.extra_data[D_HP] = pl.hp
            confirm_event.extra_data[D_POS] = pl.pos
            
            online_ids = player_center.online_ids()
            for online_id in online_ids:
                if online_id == pl.id:
                    continue
                opl = player_center.find_player(online_id)
                if not opl:
                    continue
                online_event = event.PlayerOnLineEvent(opl.id, opl.hp, opl.pos)
                online_event.extra_data[D_TO_HIDS] = [from_hid]
                self.event_producer.add_oneshot_event(online_event)
            
            other_online_event = event.PlayerOnLineEvent(pl.id, pl.hp, pl.pos)
            other_online_event.extra_data[D_TO_HIDS] = [player_center.get_hid(id) for id in online_ids \
                                                  if player_center.get_hid(id) != from_hid]
            self.event_producer.add_oneshot_event(other_online_event)
            
        confirm_event.extra_data[D_TO_HIDS] = [from_hid]
            
        self.event_producer.add_oneshot_event(confirm_event)
            
    def match(self, event_id):
        return True
    
    def handle(self, h_event):
        type = h_event.type()
        if type in self.handles:
            self.handles[type](h_event)
        else:
            h_event.set_state(event.CONTINUE_HANDLE)
        return [h_event]

class ServerUpdateFilter(Filter):
    
    def __init__(self, server_host, player_center):
        Filter.__init__(self)
        self.server_host = server_host
        self.player_center = player_center
    
    def match(self, event_id):
        if event_id == event.FrameEvent.event_id:
            return True
        return False
    def handle(self, h_event):
        h_event.set_state(event.CONTINUE_HANDLE)
        self.server_host.process()
        self.player_center.update()
        return [h_event]
        
class TimeStampFilter(Filter):
    def __init__(self):
        Filter.__init__(self)
        self.clock = CLOCK
        
    #单一的事件类型MATCH还不够安全，应该再检查输入输出数据的类型安全
    def match(self, event_id):
        return True
    
    def handle(self, h_event):
        h_event.extra_data[D_TIME_STAMP] = TIME_STAMPER.time_stamp()
        h_event.set_state(event.CONTINUE_HANDLE)
        print 'Current time stamp:%d' % h_event.extra_data[D_TIME_STAMP]
        return [h_event]

class SerialFilter(Filter):
    def __init__(self):
        Filter.__init__(self)
    def match(self, event_id):
        if event_id == event.FrameEvent.event_id:
            return True
        return True
    def handle(self, h_event):
        h_event.set_state(CONTINUE_HANDLE)
        if D_EVENT_BYTES_LIST in h_event.extra_data:
            return [h_event]
        bytes = cPickle.dumps(h_event, 1)
        h_event.extra_data[D_EVENT_BYTES_LIST] = [bytes]
        return [h_event]
    
class SockProcessFilter(Filter):
    def __init__(self, sock):
        Filter.__init__(self)
        self.sock = sock
    def match(self, event_id):
        return True
    def handle(self, h_event):
        h_event.set_state(CONTINUE_HANDLE)
        self.sock.process()
        return [h_event]
        
class DeSerialFilter(Filter):
    def match(self, event_id):
        if event_id == event.FrameEvent.event_id:
            return True
        return True
    def handle(self, h_event):
        if not D_EVENT_BYTES_LIST in h_event.extra_data:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = '%s is not in extra data.' % D_EVENT_BYTES_LIST
            return [h_event]
        
        result_events = [h_event]
        h_event.set_state(event.HANDLED)
        
        event_bytes_list = h_event.extra_data[D_EVENT_BYTES_LIST]
        for event_bytes in event_bytes_list:
            r_event = cPickle.loads(event_bytes)
            r_event.set_state(event.CONTINUE_HANDLE)
            if D_FROM_HID in h_event.extra_data:
                r_event.extra_data[D_FROM_HID] = h_event.extra_data[D_FROM_HID]
            result_events.append(r_event)
        return result_events
    
class SendFilter(Filter):
    def __init__(self, sock):
        Filter.__init__(self)
        self.sock = sock
    def match(self, event_id):
#        if event_id != event.NetSendEvent.event_id:
#            return False
        return True
    def handle(self, h_event):
        if not D_EVENT_BYTES_LIST in h_event.extra_data:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = '%s is not in extra data.' % D_EVENT_BYTES_LIST
            return [h_event]
        result_events = [h_event]
        h_event.set_state(event.HANDLED)
        for event_bytes in h_event.extra_data[D_EVENT_BYTES_LIST]:
            self.sock.send(event_bytes)
        #失败的事件创建
        return result_events
        
class ReceiveFilter(Filter):
    def __init__(self, sock):
        Filter.__init__(self)
        self.sock = sock
    def match(self, event_id):
#        if event_id == event.NetReceiveEvent.event_id:
#            return True
        return True
    def handle(self, h_event):
        h_event.extra_data[D_EVENT_BYTES_LIST] = []
        while True:
            recv_result = self.sock.recv()
            if recv_result == '':
                if len(h_event.extra_data[D_EVENT_BYTES_LIST]) == 0:
                    h_event.set_state(event.HANDLED)
                break
            else:
                h_event.extra_data[D_EVENT_BYTES_LIST].append(recv_result)
                h_event.set_state(event.CONTINUE_HANDLE)
        return [h_event]
    
class ServerReceiveFilter(Filter):
    def __init__(self, server_host):
        Filter.__init__(self)
        self.server_host = server_host
        self.handles = {
                        netstream.NET_NEW : self.__new_handle,
                        netstream.NET_DATA : self.__data_handle,
                        netstream.NET_LEAVE : self.__leave_handle
                        }
    def match(self, event_id):
        if event_id == event.FrameEvent.event_id:
            return True
        return False
    def __new_handle(self, h_event, wparam, lparam, data):
        print 'New connection:%s' % data
    def __leave_handle(self, h_event, wparam, lparam, data):
        print 'Leave connection,hid:%d' % wparam
    def __data_handle(self, h_event, wparam, lparam, data):
        if not D_EVENT_BYTES_LIST in h_event.extra_data:
            h_event.extra_data[D_EVENT_BYTES_LIST] = []
        h_event.extra_data[D_EVENT_BYTES_LIST].append(data)
        hid = wparam
        h_event.extra_data[D_FROM_HID] = hid
        print 'New data:%s' % data
    def handle(self, h_event):
        h_event.set_state(event.CONTINUE_HANDLE)
        result_events = [h_event]
        while True:
            e, wparam, lparam, data = self.server_host.read()
            if e in self.handles:
                self.handles[e](h_event, wparam, lparam, data)
            elif e == -1:
                break
        if not D_EVENT_BYTES_LIST in h_event.extra_data or \
            len(h_event.extra_data[D_EVENT_BYTES_LIST]) == 0:
            h_event.set_state(event.HANDLED)
        return result_events
    
class ServerSendFilter(Filter):
    def __init__(self, server_host):
        Filter.__init__(self)
        self.server_host = server_host
    
    def match(self, event_id):
        return True
    
    def handle(self, h_event):
        h_event.set_state(event.HANDLED)
        need_datas = [D_TO_HIDS, D_EVENT_BYTES_LIST]
        for need_data in need_datas:
            if not need_data in h_event.extra_data:
                h_event.set_state(event.ERROR_HAPPENED)
                h_event.error_msg = '%s is not in extra data.' % need_data
                return [h_event]
        for hid in h_event.extra_data[D_TO_HIDS]:
            for bytes in h_event.extra_data[D_EVENT_BYTES_LIST]:
                self.server_host.send(hid, bytes)
        return [h_event]
    
class TestPrintFilter(Filter):
    def match(self, event_id):
        return True
    def handle(self, h_event):
        print str(h_event)
        h_event.set_state(event.CONTINUE_HANDLE)
        return [h_event]
        
class KFrameLockSyncFilter(Filter):
    '''
    使用固定间隔关键帧锁定过滤器
    '''
    def __init__(self, keyframe_dur = 5):
        Filter.__init__(self)
        self.keyframe_dur = keyframe_dur
        self.next_keyframe_num = self.keyframe_dur
        self.event_list = []
    
    def match(self, event_id):
        return True
    
    def handle(self, h_event):
        if not D_TIME_STAMP in h_event.extra_data:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = '%s is not in extra data.' % D_TIME_STAMP
            return [h_event]
        result_events = [h_event]
        time_stamp = h_event.extra_data[D_TIME_STAMP]
        if time_stamp < self.next_keyframe_num:
            h_event.set_state(event.HANDLED)
            insert_pos = 0
            while insert_pos < len(self.event_list):
                t = self.event_list[insert_pos][D_TIME_STAMP]
                if time_stamp > t:
                    break
            self.event_list.insert(insert_pos, h_event)
        elif time_stamp == self.next_keyframe_num:
            h_event.set_state(event.CONTINUE_HANDLE)
            self.next_keyframe_num += self.keyframe_dur
            deliver_events = [e for e in self.event_list if e[D_TIME_STAMP] <= time_stamp]
            self.event_list = [e for e in self.event_list if e[D_TIME_STAMP] > time_stamp]
            result_events = result_events + deliver_events
        else:
            h_event.set_state(event.ERROR_HAPPENED)
            h_event.error_msg = 'Key frame %d lossed???' % self.next_keyframe_num
        return result_events
        